package com.tempjob.common.utils;

import org.springframework.util.Base64Utils;

import javax.crypto.Cipher;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;


public class RsaUtils {
    public static final String KEY_ALGORITHM = "RSA";
    public static final String SIGNATURE_ALGORITHM = "MD5withRSA";
    private static final String PUBLIC_KEY = "RSAPublicKey";
    private static final String PRIVATE_KEY = "RSAPrivateKey";
    private static volatile ConcurrentHashMap<String, Key> KEY_MAP = new ConcurrentHashMap<>(2);

    public static String privateKey = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCnCej0SNasJ42p\n" +
            "yV9tvlkfPqed9FBCc5QA/YCG61GqlsrA9F91oHUEW6BKl9JZ05Yzg3BwBhYpLVgC\n" +
            "iyTmlCd3f18TGOBUkEgpamEPcwxy33od+A4TeAaamPTw48hH57HTIAno6zrcfxJF\n" +
            "FVDEfpzEwBjxEVZOqpDNNT14S4d+qvYIgGgaCJ3dtEf2aYv96UlqitbzP9Z8AzeU\n" +
            "/KLBatcs/xnHrGWn/JI8JCe0NHQwhcYgA+arEHJ3DeoHhH1+9SrOT83+DKjW9br6\n" +
            "9TfzeX+LDq9vjQzYWrmqwApSzQyvUcgeR4cw1EFxwBOkIM2nzSVYPK1Pn6TaAOuf\n" +
            "97+0w57LAgMBAAECggEAKZASse4J1G5HFo8vuovZeyfI50mqSs6dbh2lxGx7Npos\n" +
            "uxRTSF5zn3D5EnZydb1rK0gySv2IuBg7RO7AcMs+gsJjssnGz+PSwNNicUisOU0Y\n" +
            "Aqr9PaTo58DVebcD17h0C9d1/8nALKtFrYDfRvVGJsKxshIhSpOwUg9ES1Fn1oFm\n" +
            "gwYRKg4+BM5e0KJvfa+e3n9sAVVbQZSVTcMEKOoGDIIIZN2N654wj7E6rdRhIar+\n" +
            "SdNvJ+GcMXIl8xjDXfQfcR/0U1whufA8CXQVZOvqS3sJD2NoNNqwmt4c7AsMhm4+\n" +
            "rPyGF24y0PSrcPypMD8eFESvTEINccvrv2ZLETpIyQKBgQDaqHjq5/XwLngzmqiQ\n" +
            "qxXzjCZcyb6IfNwiFBNZwW/D7/b1ryAGhVxNtyITh68LVSKc+/G+/K/WJhpc6D+p\n" +
            "jc3ONyNl4oJnLiUmMwgwW89oUHRedceXzPhUc8tNueWuHPVv6zH3d7el60GUHtVB\n" +
            "6WCLgQSFlQaR5YQyFNT/+qNmdQKBgQDDkK+21u9m8e7E3BZhkVodzngn02xKWwZC\n" +
            "eaNBDrQN8IfGC80+Ch5XfExwJvJ4ObAd50YeRcu44xoF04LUry4HBz2nDgQn+aH4\n" +
            "zYNE9Xm4KqXMoNiZsm/Lf8l9aED0FFGe3y/TAj9wcgv0HvQBuLyItRABKGvNrb4m\n" +
            "FG/GiDrIPwKBgDvI+p7H44/TNNxvOg2sKalYtZiDNMxez1WmBZBx+lwpfW25f1kp\n" +
            "F47MNs3+OFcx5O/lVzjwICe+bVDf06BJfG8Y1l3iK+OIU3H5XBDU2YXdEaROMc+I\n" +
            "pJF6w6ix3mHX2yfJt/TAz/7WryyPraGC7PnXwePAVnaHubhzx7Vh01GNAoGBAL1i\n" +
            "nmdoGgsNe3a01A/EXQrEaePXgOU45QT3Grvj60Ycir+lqirMTbnXK4bokKMQ6pbq\n" +
            "4yJp4PG5K/g8blRdGSvBPVlL34Z+3vY8v0TGnAtXcZyKn3eoL09Y/t0tWsmD6874\n" +
            "wC2N3bWdHCpa113bcujIvyj/5fYWrS/fVqdiQdqtAoGAUX4VjHjcWrimPilDVl6/\n" +
            "QUezAafWAZOe/MrZmbG3v21vWc+y/FKkPfAXCtlPXOD9+Ux7sBdSiaroPbp4tIne\n" +
            "e7OtLPH4z5JBy9FmCJOEwH8qq39CoDVqWoYOPdTE4lN7N7tISVEHQ4Y1bFcFO03a\n" +
            "II44nGAWATwR4C/hCWln+h0=";

    public static String publicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApwno9EjWrCeNqclfbb5Z\n" +
            "Hz6nnfRQQnOUAP2AhutRqpbKwPRfdaB1BFugSpfSWdOWM4NwcAYWKS1YAosk5pQn\n" +
            "d39fExjgVJBIKWphD3MMct96HfgOE3gGmpj08OPIR+ex0yAJ6Os63H8SRRVQxH6c\n" +
            "xMAY8RFWTqqQzTU9eEuHfqr2CIBoGgid3bRH9mmL/elJaorW8z/WfAM3lPyiwWrX\n" +
            "LP8Zx6xlp/ySPCQntDR0MIXGIAPmqxBydw3qB4R9fvUqzk/N/gyo1vW6+vU383l/\n" +
            "iw6vb40M2Fq5qsAKUs0Mr1HIHkeHMNRBccATpCDNp80lWDytT5+k2gDrn/e/tMOe\n" +
            "ywIDAQAB";

    /**
     * 加密<br>
     * 用公钥加密
     *
     * @param data //     * @param key
     * @return
     * @throws Exception
     */
    public static String encryptByPublicKey(String data)
            throws Exception {
        // 对公钥解密
        byte[] keyBytes = decryptBASE64(publicKey);
        // 取得公钥
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key publicKey = keyFactory.generatePublic(x509KeySpec);
        // 对数据加密
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        return Base64.getEncoder().encodeToString(cipher.doFinal(data.getBytes()));
    }

    /**
     * 通过私钥解密
     *
     * @param data       加密的byte数组
     * @param privateKey 私钥
     * @return byte[]
     * @author compass
     * @date 2022/9/1 17:14
     * @since 1.0.0
     **/
//    public static String decryptByPrivateKey(String data, String privateKey)
//            throws Exception {
//        return new String(decryptByPrivateKey(decryptBASE64(data), privateKey));
//    }

    /**
     * 通过私钥解密
     *
     * @param data 加密的byte数组
     *             //     * @param privateKey 私钥
     * @return byte[]
     * @author compass
     * @date 2022/9/1 17:14
     * @since 1.0.0
     **/
    public static String decryptByPrivateKey(String data)
            throws Exception {
        // 对密钥解密
        byte[] keyBytes = decryptBASE64(privateKey);
        // 取得私钥
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key privatizationKey = keyFactory.generatePrivate(pkcs8KeySpec);
        // 对数据解密
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, privatizationKey);
        byte[] dataByte = decryptBASE64(data);
        return new String(cipher.doFinal(dataByte));

    }

    /**
     * base64解码
     *
     * @param content
     * @return byte[]
     * @author compass
     * @date 2022/9/1 17:12
     * @since 1.0.0
     **/
    public static byte[] decryptBASE64(String content) {
        return Base64.getMimeDecoder().decode(content.getBytes());
    }

    /**
     * base64编码
     *
     * @param bytes 字符byte数组
     * @return java.lang.String
     * @author compass
     * @date 2022/9/1 17:12
     * @since 1.0.0
     **/
    public static String encryptBASE64(byte[] bytes) {
        return new String(Base64Utils.encode(bytes));
    }

    /**
     * 用私钥对信息生成数字签名
     *
     * @param data       加密数据
     * @param privateKey 私钥
     * @return java.lang.String
     * @author compass
     * @date 2022/9/1 14:21
     * @since 1.0.0
     **/
    public static String sign(byte[] data, String privateKey) throws Exception {
        // 解密由base64编码的私钥
        byte[] keyBytes = decryptBASE64(privateKey);
        // 构造PKCS8EncodedKeySpec对象
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        // KEY_ALGORITHM 指定的加密算法
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        // 取私钥匙对象
        PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);
        // 用私钥对信息生成数字签名
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initSign(priKey);
        signature.update(data);
        return encryptBASE64(signature.sign());
    }

    /**
     * 校验数字签名
     *
     * @param data      加密数据
     * @param publicKey 公钥
     * @param sign      数字签名
     * @return 校验成功返回true 失败返回false
     * @throws Exception
     */
    public static boolean verify(byte[] data, String publicKey, String sign)
            throws Exception {
        // 解密由base64编码的公钥
        byte[] keyBytes = decryptBASE64(publicKey);
        // 构造X509EncodedKeySpec对象
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        // KEY_ALGORITHM 指定的加密算法
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        // 取公钥匙对象
        PublicKey pubKey = keyFactory.generatePublic(keySpec);
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initVerify(pubKey);
        signature.update(data);
        // 验证签名是否正常
        return signature.verify(decryptBASE64(sign));
    }


//    /**
//     * 私钥解密前端jsencrypt分段加密的长文本内容,
//     * 前端加密的字符串需要使用encodeURIComponent编码,后端使用URLDecoder.decode解码来解决中文字符串无法解密的问题
//     *
//     * @param content    加密的内容
//     * @param privateKey 私钥
//     * @return java.lang.String
//     * @author compass
//     * @date 2022/9/1 16:53
//     * @since 1.0.0
//     **/
//    public static String jsencryptDecryptByPrivateKeyLong(String content, String privateKey) {
//        String resultContent = "";
//        try {
//            StringBuffer buffer = new StringBuffer();
//            if (content != null && content.trim().length() > 0) {
//                String[] contentList = content.split("=");
//                for (String item : contentList) {
//                    byte[] itemBytes = Base64Utils.decode((item + "=").getBytes());
//                    try {
//                        String itemResultStr = decryptByPrivateKey(itemBytes, privateKey);
//                        buffer.append(itemResultStr);
//                    } catch (Exception e) {
//                        e.printStackTrace();
//                    }
//                }
//
//            }
//            resultContent = URLDecoder.decode(buffer.toString());
//        } catch (Exception e) {
//            e.printStackTrace();
//            System.err.println("jsencryptDecryptByPrivateKeyLong解密出错:" + e.getMessage() + ":" + "解密内容:" + content);
//            throw new RuntimeException("rsa解密失败");
//        }
//        return resultContent;
//    }


    /**
     * 解密<br>
     *
     *
     * @param data
     * @param key
     * @return
     * @throws Exception
     */
    /**
     * 用公钥解密
     *
     * @param data      代解密的byte数组
     * @param publicKey
     * @return byte[]
     * @author compass
     * @date 2022/9/1 17:17
     * @since 1.0.0
     **/
    public static byte[] decryptByPublicKey(byte[] data, String publicKey)
            throws Exception {
        // 对密钥解密
        byte[] keyBytes = decryptBASE64(publicKey);
        // 取得公钥
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key publicityKey = keyFactory.generatePublic(x509KeySpec);
        // 对数据解密
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, publicityKey);
        return cipher.doFinal(data);
    }


    /**
     * 加密<br>
     * 用私钥加密
     *
     * @param data
     * @param key
     * @return
     * @throws Exception
     */
    public static byte[] encryptByPrivateKey(byte[] data, String key)
            throws Exception {
        // 对密钥解密
        byte[] keyBytes = decryptBASE64(key);
        // 取得私钥
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
        // 对数据加密
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);
        return cipher.doFinal(data);
    }

    /**
     * 获取私钥
     *
     * @param keyMap 存放私钥和公钥的map集合
     * @return java.lang.String
     * @author compass
     * @date 2022/9/1 17:18
     * @since 1.0.0
     **/
    public static String getPrivateKey(Map<String, Key> keyMap) {
        Key key = keyMap.get(PRIVATE_KEY);
        return encryptBASE64(key.getEncoded());
    }

    /**
     * 取得公钥
     *
     * @param keyMap 放私钥和公钥的map集合
     * @return java.lang.String
     * @author compass
     * @date 2022/9/1 17:28
     * @since 1.0.0
     **/
    public static String getPublicKey(Map<String, Key> keyMap) {
        Key key = keyMap.get(PUBLIC_KEY);
        return encryptBASE64(key.getEncoded());
    }

    /**
     * 初始化公钥和秘钥 每次调用可以获取不同的公钥和私钥
     *
     * @return java.util.Map<java.lang.String, java.security.Key>
     * @author compass
     * @date 2022/9/1 17:28
     * @since 1.0.0
     **/
    public static ConcurrentHashMap<String, Key> initKey() throws Exception {
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
        keyPairGen.initialize(1024);
        KeyPair keyPair = keyPairGen.generateKeyPair();
        ConcurrentHashMap<String, Key> keyMap = new ConcurrentHashMap<>(2);
        keyMap.put(PUBLIC_KEY, keyPair.getPublic());// 公钥
        keyMap.put(PRIVATE_KEY, keyPair.getPrivate());// 私钥
        return keyMap;
    }

    /**
     * 初始化公钥和秘钥 初始化唯一的公钥和私钥
     *
     * @return java.util.Map<java.lang.String, java.security.Key>
     * @author compass
     * @date 2022/9/1 17:28
     * @since 1.0.0
     **/
    public static Map<String, Key> initKeyOnce() {
        if (KEY_MAP.size() == 0) {
            synchronized (RsaUtils.class) {
                if (KEY_MAP.size() == 0) {
                    try {
                        KEY_MAP = initKey();
                    } catch (Exception e) {
                        e.printStackTrace();
                        throw new RuntimeException("公钥和私钥初始化失败");
                    }
                }
            }
        }
        return KEY_MAP;
    }

    public static void main(String[] args) throws Exception {
//        String publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCWh3Nyt+5QqUXw1qHXM4k7lq98f9wA4iQgKK1LB1tr4uIgL/dls0LkBgY4oS/Dn3J0qHkpUTkTT84uMHey7cwdd9k90/65cpdawX0J0KO3S3Zwl9d5AJt7/hdSap3AcHw3dvlrZvvDJ72AaR3YUPujNM3dhLC7tsdDb3CxoJSBDQIDAQAB";
//        String privateKey = "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAJaHc3K37lCpRfDWodcziTuWr3x/3ADiJCAorUsHW2vi4iAv92WzQuQGBjihL8OfcnSoeSlRORNPzi4wd7LtzB132T3T/rlyl1rBfQnQo7dLdnCX13kAm3v+F1JqncBwfDd2+Wtm+8MnvYBpHdhQ+6M0zd2EsLu2x0NvcLGglIENAgMBAAECgYAsR/ZXRfJOOi1/9rOvSdLR+7bt6fL/M4crCqxHyQdEyn54t4OQoFZKG9eSqyAQ7QPPe4wA8orWuoBNqCZeNYP4pXV2ayPwZcUSN9SX4/ce5QZkhHDVBwC8SIQQ7osU6Joh4gR3I+CHlmM1dCItBizOC0Jw4Scs7cpnzzMgYhdPoQJBAO9gzFvGBROOMwtqmOU7adbbM8FE8LRHnRrKv6OvX3Qs5Kqu4vFY78LW4tPzxbzAdEMAF9rltPcc3Y9D8U8Am7UCQQCg+0Q/Za+HQ5Tgbv9QGYI1tvTUe6WiC3VHcUGmQIqa78baEd7pndcPZuqbnAPVw4oWsuhQEXSakuL+KLGJXZb5AkBE2sANidj99gIiv4e5MCzSe3zYk970zECZa0ZSa+h1/0/K9MEckOtuTOcz9kOjdmw6tXUnJrm19tyYEAACLHedAkAyrATmg8aFqFMzdhzthKoE6GsWezk+0aZ/73l/sG8wp+sK93cYSDPKyFVu1+QpJFzSGkyf726pvTSwVfTUTV5ZAkBWX+yR7VdY3e55rQBQg8k0XhFcldbaN1rZz+a41+smvpxwlslxI+ERH1yY2COUxoZIiD9VhGWudvjca+0tRgXA";

        String inputStr = "aADMIN@9527";
        String encodedData = RsaUtils.encryptByPublicKey(inputStr);
//        String decodedData = RsaUtils.decryptByPrivateKey(encodeString);
        System.out.println("加密前: " + inputStr + "\n\r" + "加解密后: " + encodedData);
//        System.out.println("解密后" + decodedData);

    }
}
